Handle unhandled Promise rejections in JavaScript#65
Handle unhandled Promise rejections in JavaScript#65
Conversation
|
oh gawd this is a rabbit hole... so catching unhandled rejections through |
|
okay wait i think this makes life easier, i just have to check for nodejs-style catching of unhandled rejections, force push coming soon |
25cf70f to
3a0b018
Compare
|
ok updated! while i was at it i added logic so that process exit occurs at i also now made it so that all unhandled rejections are assumed to be failures, even if the program itself subscribes to unhandled rejections or handles it at a point where the program would normally crash without subscribing. i figured that logic wasn't worth keeping since realistically promise rejection should only happen when you WANT an exception to occur in the test. though i'll update the description because both behaviors to me make sense. |
lpil
left a comment
There was a problem hiding this comment.
Thank you! Fantastic work! I've left some questions inline.
| javascript_test_delayed_promise() | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
I don't understand this test, what is it doing? Why is it reading environment variables and spawning OS processes?
There was a problem hiding this comment.
It respawns gleam test but using the JavaScript runtime and with an environment variable, as the test in question requires seeing if gleeunit exits with an error on an uncaught Promise rejection. Since there are two tests (immediate unhandled rejection, delayed unhandled rejection), it's spawned two times with the environment variable set for the two test cases to run. I was considering either this or making an OS temporary directory with a brand-new Gleam project, but figured this was easier to implement.
| // The maximum amount of time running all tests is expected to take. | ||
| // This is used to set the timeout for the delayed promise test, to ensure | ||
| // it runs after all other tests have had a chance to execute. | ||
| const MAX_TEST_TIME = 4000; |
There was a problem hiding this comment.
This comment is a bit confusing to me, it sounds like it is being used as the timeout for tests being run, but it seems to instead be used to reject one promise in the FFI below?
There was a problem hiding this comment.
The intention is for the Promise's rejection to outlast the regular time of the tests, as it's testing to make sure gleeunit doesn't prematurely exit before the Promise rejects.
| export function delayed_promise_fail_test() { | ||
| new Promise((_, reject) => { | ||
| setTimeout(() => { | ||
| reject(new Error("Promise panicked")); |
There was a problem hiding this comment.
This says the promise panics, but there's no panic here.
There was a problem hiding this comment.
Got it, i'll update the message!
| }; | ||
|
|
||
| // If we're able to subscribe to beforeExit, then we can report unhandled rejections that | ||
| // occur after the tests are finished. |
There was a problem hiding this comment.
Very cool! Which runtimes is this API not available for?
There was a problem hiding this comment.
Node, Deno, and Bun all cover it, so the edge case is if someone decides to run it on a Vercel Edge runtime or some really early version of Deno/Bun or a browser or something :p i can remove it if you want
Fixes #62
About
On all major server-side JavaScript runtimes, unhandled rejections of Promises by default lead to a fatal error and the program prematurely exiting. This can be demonstrated in pure Gleam:
However, attaching an event listener to unhandled rejections changes this behavior, and allows for detecting these errors. This PR handles these rejections and appends it as a failed test case from an unknown module and unknown function.
For testing, this PR also adds two dev dependencies
envoyandshellout, as testing requires the test runner explicitly failing.Caveats
This code assumes that ALL unhandled rejections within tests should be considered as failures. In some programs this may not be the case, as unhandled rejections don't ALWAYS lead to program exit if the program also subscribes to unhandled rejections through
process.on. Some Gleam programs may want this behavior, but as it would lead toasserts andpanics within unhandled rejections not being recognized. This code could be changed to either: